from datetime import datetime, date
from pathlib import Path
from typing import List, Optional

from pydantic import BaseModel, ValidationError, constr

from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.orm import declarative_base


class User(BaseModel):
    id: int  # 必填字段
    name: str = "GuguLH"  # 默认值,选填字段
    signup_ts: Optional[datetime] = None  # 不填就是None
    friends: List[int] = []  # 列表中的元素是int类型或者可以直接转换成int类型


external_data = {
    "id": "123",
    "signup_ts": "2025-06-18 14:31",
    "friends": [1, "2", 3, 4, 5, "6"]
}

# 解构语法
user = User(**external_data)

print(user.id, user.friends)
print(repr(user.signup_ts))
print(user.dict())

print("\033[31m2. --- 校验失败处理 --- \033[0m")
try:
    User(id=1, signup_ts=datetime.today(), friends=[1, 2, 3, "Not number"])
except ValidationError as e:
    print(e.json())

print("\033[31m3. --- 模型类的属性和方法 --- \033[0m")
print(user.dict())
print(user.json())
print(user.copy())  # 这里是浅拷贝
print(User.parse_obj(obj=external_data))
print(
    User.parse_raw('{"id": 123, "name": "GuguLH", "signup_ts": "2025-06-18T14:31:00", "friends": [1, 2, 3, 4, 5, 6]}'))

path = Path('pydantic_tutorial.json')
path.write_text('{"id": 123, "name": "GuguLH", "signup_ts": "2025-06-18T14:31:00", "friends": [1, 2, 3, 4, 5, 6]}')
print(User.parse_file(path))

print(user.schema())
print(user.schema_json())

user_data = {"id": "error", "name": "GuguLH", "signup_ts": "2025-06-18T14:31:00", "friends": [1, 2, 3, 4, 5, 6]}
print(User.construct(**user_data))  # 不校验数据直接创建模型类,不建议在construct方法中传入未经验证的数据
print(User.__fields__.keys())  # 定义模型类的时候,所有字段都注明类型,字段的顺序就不会乱

print("\033[31m4. --- 递归模型 --- \033[0m")


class Sound(BaseModel):
    sound: str


class Dog(BaseModel):
    birthday: date
    weight: float = Optional[None]
    sound: List[Sound]  # 不同狗有不同的叫声.递归模型(Recursive Models) 就是值一个嵌套一个


dogs = Dog(birthday=date.today(), weight=6.66, sound=[{"sound": "wangwang~"}, {"sound": "wuwu..."}])
print(dogs.dict())

print("\033[31m5. --- ORM模型: 从类实例创建符合ORM对象的模型 --- \033[0m")

Base = declarative_base()


class CompanyOrm(Base):
    __tablename__ = "companies"
    id = Column(Integer, primary_key=True, nullable=False)
    public_key = Column(String(20), index=True, nullable=False, unique=True)
    name = Column(String(63), unique=True)
    domains = Column(ARRAY(String(255)))


class CompanyMode(BaseModel):
    id: int
    public_key: constr(max_length=20)
    name: constr(max_length=63)
    domains: List[constr(max_length=255)]

    class Config:
        orm_mode = True


co_orm = CompanyOrm(
    id=123,
    public_key="foobar",
    name="Testing",
    domains=["gugulh.com", "gugulh.top"]
)

print(CompanyMode.from_orm(co_orm))
